/****************************************************************************
*  Title: simple_edge.sc
*  Author: Bryant Smith
*  Date: 04/16/2011
*  Description: takes in an 8bpp image array row by row over input c_queue
*			    and uses a sobel mask for edge detection then returns
*				the image to the an output c_queue once edge detection is 
*				performed
*
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


// define image size, idea being from a hw camera this would be fixed
#define IMG_ROWS 480
#define IMG_CLMS 640

import "c_queue";


/*
* Receives imput image from c_queue
*/
behavior receiveImage(i_receiver InQueue, out unsigned char image_buffer[IMG_ROWS][IMG_CLMS]) 
{
	// image buffers these are currently dependent on the size and format of the image used
	unsigned char row_buffer_1[IMG_CLMS]; // this file is 4x4 since there are 3 bytes/pixel -> 1536 bytes per row
	int row, a, j;

	void main(void) 
	{				
		printf("Reading Image\n");
		
		//********************
		// Load image from c_queue into memory
		//********************
		
		// read image from c_queue and load into image buffer
		for(row = 0; row < IMG_ROWS; row++) {

			// Load row with a row of the image
			InQueue.receive(row_buffer_1, (IMG_CLMS));	
			
			// place row into image buffer
			for(a = 0; a < (IMG_CLMS); a++){
				image_buffer[row][a] =  row_buffer_1[a]; 	
			}			
			
			// generate grayscale image matrix from color RGM input image
//			for(j = 0; j < (IMG_CLMS); j = j++)
//			{
//				// create 8 bpp grayscale image with weight of .3R + .59G + .11B
//				image8bpp[row][j/3] = (unsigned char) (0.3 * (row_buffer_1[j]) + 0.59 * (row_buffer_1[j+1]) + 0.11 * (row_buffer_1[j+2]));
//			}
		}		
		printf("Finished reading image into memory.  Starting Image Processing . . . .\n");
		
	}
};


/*
* Calculate image differentials
*/
behavior preProcessImage( in unsigned char image8bpp[IMG_ROWS][IMG_CLMS], out double diffxy[IMG_ROWS][IMG_CLMS])
{

	int masksize = 3;
	int convolveX[3][3] = {{-1, 0, 1}, {-1, 0, 1}, {-1, 0, 1}};
	int convolveY[3][3] = {{-1, -1, -1}, {0, 0, 0}, {1, 1, 1}};
	int x, y, x_1, y_1, posx, posy, imPosx, imPosy;
	int width, height;

	double diffx[IMG_ROWS][IMG_CLMS];
	double diffy[IMG_ROWS][IMG_CLMS];		
	double valx, valy;

	void main(void) 
	{
	
		width = IMG_CLMS;
		height = IMG_ROWS;
		//********************
		// Apply x, y, xy gradient convolution
		//********************			
		for(x = masksize / 2; x < width - (masksize / 2); x++) {  //columns
			for(y= masksize / 2; y < height- (masksize / 2); y++) { // rows
				valx = 0;
				valy = 0;
				for( x_1 = 0; x_1 < masksize; x_1++) {
					for( y_1 = 0; y_1 < masksize; y_1++) {
						imPosx = (x + (x_1 - (masksize / 2)));
						imPosy = (y + (y_1 - (masksize / 2)));
						
						valx += ((image8bpp[imPosy][imPosx]) * convolveX[y_1][x_1]);
						valy += ((image8bpp[imPosy][imPosx]) * convolveY[y_1][x_1]);
					}
				}
				diffx[y][x] = valx * valx;  // horizontial gradient
				diffy[y][x] = valy * valy;  // vertical gradient
				diffxy[y][x] =  valx * valy;  // xy gradient
			}
		}	
	}
};


/*
* Performs sobel edge detection
*/
// could split into sobel-x and sobel-y if placing on seperate hardware
// not done here, since this will be mapped to a single PE there is no sense in replicating loops and counters
behavior sobelEdge( in unsigned char image8bpp[IMG_ROWS][IMG_CLMS], out double sobelG[IMG_ROWS][IMG_CLMS])
{

	int masksize = 3;
	int sobelX[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
	int sobelY[3][3] = {{1, 2, 1}, {0, 0, 0}, {-1, -2, -1}};
	int x, y, x_1, y_1, posx, posy, imPosx, imPosy;
	int width, height;

	//double Gx[IMG_ROWS][IMG_CLMS];
	//double Gy[IMG_ROWS][IMG_CLMS];		
	double valx, valy, Gxy;

	void main(void) 
	{
	
		width = IMG_CLMS;
		height = IMG_ROWS;
		//********************
		// Apply x, y, xy gradient convolution
		//********************			
		for(x = masksize / 2; x < width - (masksize / 2); x++) {  //columns
			for(y= masksize / 2; y < height- (masksize / 2); y++) { // rows
				valx = 0;
				valy = 0;
				for( x_1 = 0; x_1 < masksize; x_1++) {
					for( y_1 = 0; y_1 < masksize; y_1++) {
						imPosx = (x + (x_1 - (masksize / 2)));
						imPosy = (y + (y_1 - (masksize / 2)));
						
						valx += ((image8bpp[imPosy][imPosx]) * sobelX[y_1][x_1]);
						valy += ((image8bpp[imPosy][imPosx]) * sobelY[y_1][x_1]);
						//printf("valx %f\n", valx);
					}
				}
				
				sobelG[y][x] =  sqrt(valx * valx + valy * valy);  // gradient magnitude
				
				// can approximate magniture and normalization for edge (Myler method)
				//Gx[y][x] = valx * valx;  // horizontial gradient
				//Gy[y][x] = valy * valy;  // vertical gradient
				
				//Gxy = abs(valx) + abs(valy);
				//if (Gxy>255) Gxy=255;
				//sobelG[y][x] = (unsigned char) Gxy;				
			}
		}	
	}
};



/*
* used to directly write out an image after normalizaing for debug purposes
*/
behavior debugSender(in double debug[IMG_ROWS][IMG_CLMS]) 
{
	// File names
	char OUTPUT_BMP[] = "output8bpp.bmp"; // output file name
	
	int row, wt_cnt, i, j;
	unsigned char pixel;
	double max, min, normFact;
	unsigned char edgeImage[IMG_ROWS][IMG_CLMS];
		
	void main(void) {
		
		//File variable
		FILE * wptr;
		
		// normalize not yet used 
		// find min an max value
		max = 0;
		min = 0;
		for(i = 0; i < IMG_ROWS; i++) 
		{
			for(j = 0; j < IMG_CLMS; j++)
			{
				if ( debug[i][j] > max) max =  debug[i][j];
				if ( debug[i][j] < min) min =  debug[i][j];
			}
		}
		
		normFact = (max - min) / 255;
  
		// open output file file
		wptr = fopen(OUTPUT_BMP, "a"); // append to file which already has header from stimulus
  

		for(row = 0; row < IMG_ROWS; row++)
		{
			
			for(wt_cnt = 0; wt_cnt < IMG_CLMS; wt_cnt++)
			{
				pixel = (unsigned char) (( (debug[row][wt_cnt]) - min) / normFact);
				fprintf(wptr,"%c",pixel);                                    
				
				edgeImage[row][wt_cnt] = pixel;
			}
			
		}
  
	fclose(wptr);
	printf("debug image completed check file %s\n", OUTPUT_BMP);
	}
};


/*
* Normalizes image of array double and sends out pixels over c_queue, assumes
* image pixels are magnitude only, no negative values
*/
behavior normalizeSender(in double diffxy[IMG_ROWS][IMG_CLMS], i_sender OutQueue) 
{
	int row, wt_cnt, i, j;
	double val, max, normFact;
	unsigned char edgeImage[IMG_ROWS][IMG_CLMS];
		
	void main(void) {
		
		// normalize not yet used 
		// find min an max value
		max = 0;
		//min = 0;
		for(i = 0; i < IMG_ROWS; i++) 
		{
			for(j = 0; j < IMG_CLMS; j++)
			{
				if (diffxy[i][j] > max) max = diffxy[i][j];
			}
		}
		
		normFact = max / 255;

		for(row = 0; row < IMG_ROWS; row++)
		{
			
			for(wt_cnt = 0; wt_cnt < IMG_CLMS; wt_cnt++)
			{
				edgeImage[row][wt_cnt] = (unsigned char) ((diffxy[row][wt_cnt]) / normFact);
			}
			
			// send image out
			OutQueue.send(edgeImage[row], (IMG_CLMS));
		}

	printf("Image has been sent out\n");
	}
};



behavior edgeDetect(i_receiver InQueue, i_sender OutQueue)
{
	// image buffers these are currently dependent on the size and format of the image used
	unsigned char img_buf[IMG_ROWS][IMG_CLMS];
	double sobelG[IMG_ROWS][IMG_CLMS];
	double diffxy[IMG_ROWS][IMG_CLMS];
	
	receiveImage    imgRcvr(InQueue, img_buf);
	preProcessImage preProc(img_buf, diffxy);
	sobelEdge sobel(img_buf, sobelG);
	debugSender			dbgSndr(diffxy);
	normalizeSender     imgSender(sobelG, OutQueue);

	void main(void) 
	{	
		imgRcvr.main();
		preProc.main();
		dbgSndr.main();
		sobel.main();
		imgSender.main();
	}
	
};
